4. Impersonation
Impersonation is a
commonly used technique, especially within a distributed application.
The basic idea is that a service is actually performing an action at
the request of a client. However, due to some of the restrictions that
were discussed at the beginning of this lesson (such as process
identity), it is possible that the service doesn’t have access to all
the resources required to fulfill the request. However, suppose the
client does have the appropriate rights. Wouldn’t it be useful for the
service to assume the rights of the client? After all, if the client
were accessing the resources directly, it would have the necessary
rights.
The purpose of impersonation
is to extend the access of a service to resources that might be
off-limits. It does this by taking the rights of the requester into
consideration. Impersonation enables the service to assume the security
context of the requester when it must determine whether access to a
particular resource is to be allowed.
4.1. Impersonation Level
Given
that impersonation involves the temporary transfer of rights, the
question becomes how far those rights extend. Does impersonation allow
the service to use all the rights of the client across the entire
network, or is it restricted to accessing local resources? The answer
is specified by the impersonation level. This value is provided at the
point at which impersonation is defined, and it describes the scope of
the impersonation process. Table 4 contains a list of the valid impersonation levels and their meanings.
Table 4. AllowedImpersonationLevel Values
Impersonation Level | Description |
---|
None | No impersonation level is provided. |
Anonymous | No impersonation is allowed. In fact, the service will not be able to obtain any identification information about the client. |
Identification | The
service is able to obtain identification information about the client,
including username and privileges, but the service is not able to make
any resource requests on behalf of the client. The service will be
restricted to those resources it is allowed access to. |
Impersonation | The
service is able to make requests for resources, using the client’s
credentials on the local system. This means that the service might be
able to access resources it would not be able to access on its own.
However, this capability is restricted to local resources. |
Delegation | The service is able to make requests for resources both locally and on remote systems. |
Note:
For impersonation to take place, the request must have a Windows
identity. Without a Windows identity, it is not possible to extend the
rights of the service. This makes sense because if the client doesn’t
have a Windows identity, there are no additional rights that could be
conveyed to the service.
4.2. Transport-Level Impersonation
Impersonation within WCF can
take place at two different levels. Transport impersonation means that
the credentials used to secure the transport layer are provided to the
service to control access. The most common types of impersonation at
the transport layer are Identify and Impersonate. The None and Anonymous levels are not recommended for use and are not supported by many transport protocols. The Delegate level is a very powerful option and should be used only when the client and the service are both trusted applications.
For the service to use Impersonate or Delegate levels, it needs to have the SetImpersonatePrivilege privilege set. Although any user can be configured to have this privilege, by default, it is available only
to members of the Administrators group or accounts that have the
Service SID (Local System, Local Service, or Network Service).
Table 5. Relationship Between HTTP Authentication and SOAP-Based Impersonation
Authentication Scheme | Supported Impersonation Levels |
---|
Anonymous | None. |
Basic | Delegate only. All lower levels are upgraded to Delegate. |
Digest | Impersonate and Delegate. |
NTLM | Delegate. |
Kerberos | All. |
As was already mentioned,
the transport choice affects the ability to perform impersonation. For
example, if named pipes are used as the transport, impersonation and
delegation are not supported. If HTTP is used as the transport, the
limitations on impersonation become related to the authentication
scheme. Table 5 shows the authentication schemes and the different levels of impersonation that are allowed.
The second technique used to
implement impersonation involves the use of SOAP. Keep in mind the
requirement that a Windows identity must be available to the service,
so any request needs to have information about the identity included in
the metadata associated with the request. SOAP accomplishes this by
using one of two methods, cached token impersonation and
Service-for-User (S4U) impersonation.
4.2.1. Cached Token Impersonation
When an incoming call is
processed, a token is requested from the Security Support Provider
Interface (SSPI) or Kerberos authenticator. This is a token associated
with a Windows identity and is then cached at the service so that
future requests from the same user can be processed more efficiently.
Cached token impersonation is available to some types of bindings.
wsHttpBinding, wsDualHttpBinding, and netTcpBinding when the client presents a Windows credential.
basicHttpBinding and basicHttpSecurityBinding when the security mode is set to TransportWithMessageCredentials. In this case, the credentials provided must map to a Windows identity.
Any custom binding when a Windows client credential is presented and requireCancellation is set to true. The requireCancellation
attribute ensures that the validation of the identity is performed with
every request, enabling the caller’s information to be populated into
the Windows identity element.
Any custom binding when a UserName client credential is presented and can be mapped to a Windows user. Again, the requireCancellation attribute must be set to true, and the credentials must use a provider that validates against a Windows domain.
4.2.2. Service-for-User (S4U) Impersonation
The
idea behind S4U impersonation is that the token retrieved from Kerberos
contains information about the type of impersonation that can be
performed—the token that arrives at the service is used directly to
provide the required level of impersonation. The types of bindings that
can use this impersonation are:
wsHttpBinding, wsDualHttpBinding, and netTcpBinding when the client presents a certificate credential that can be mapped to a Windows identity.
A custom binding when a Windows client credential is presented and requireCancellation is set to false. This last requirement enables the token information to be flowed from the client to the service.
A custom binding requiring a secure conversation and providing UserName credentials. Again, requireCancellation must be set to false.
4.3. Implementing Impersonation
In most cases, impersonation
is driven by the need for the service to apply client credentials. For
this reason, the configuration options for impersonation take place
within the service. The simplest way to implement impersonation is
declaratively on the service’s methods. The OperationBehavior attribute includes a property called Impersonation. This property can be set to Required or Allowed. The following code demonstrates this.
' VB
<OperationBehavior(Impersonation := ImpersonationOption.Allowed)> _
Public Function Update() As Boolean
Return True
End Function
// C#
[OperationBehavior(Impersonation = ImpersonationOption.Allowed)]
public bool Update()
{
return true;
}
If the Impersonation property is set to Allowed, the client credentials can flow to the service. If Impersonation is set to Required, the service must assume the client’s credentials.
Note: If Impersonation is set to Required, the service will assume the rights associated with the client even if the service actually has a higher set of privileges. If Impersonation is set to Allowed, the service will use the client rights only if the service rights don’t allow access.
There are times when not all
of a method might require impersonation. Perhaps impersonation is
required only when a file is being accessed, for example. To allow for
this, it is possible to implement impersonation imperatively by using
the WindowsImpersonationContext class.
To start, you must retrieve the Windows identity associated with the current request. This is available through the ServiceSecurityContext.Current object. If the WindowsIdentity property is not null (remembering that a Windows identity is required for impersonation), you can invoke the Impersonate method on the identity. The following code demonstrates this technique:
' VB
Dim callerIdentity As WindowsIdentity = _
ServiceSecurityContext.Current.WindowsIdentity
If (callerIdentity Is Nothing) Then
Throw New InvalidOperationException( _
"The caller cannot be mapped to a WindowsIdentity")
End If
Dim context As WindowsImpersonationContext = _
callerIdentity.Impersonate()
Using (context)
' Access a file as the caller.
End Using
// C#
WindowsIdentity callerIdentity =
ServiceSecurityContext.Current.WindowsIdentity;
if (callerIdentity == null)
throw new InvalidOperationException(
"The caller cannot be mapped to a WindowsIdentity");
using (WindowsImpersonationContext context = callerIdentity.Impersonate())
{
// Access a file as the caller.
}
Although not required, it is a good idea to use a using
statement when performing impersonation. This enables the statements
that use impersonation to be clearly bounded. If you choose not to use
the using construct, you increase the chance for subtle security bugs to creep into your application.
The two impersonation
techniques demonstrated so far operate on a method-by-method basis. It
is also possible to enable impersonation for all methods in a service.
You do this by setting the ImpersonateCallerForAllOperations property on the ServiceAuthorization behavior to true. You can do this as shown in the following code sample:
' VB
Dim _serviceHost As New ServiceHost(GetType(TestService))
Dim behavior As ServiceAuthorizationBehavior = _
_serviceHost.Description.Behaviors.Find _
(Of ServiceAuthorizationBehavior)()
behavior.ImpersonateCallerForAllOperations = True
// C#
ServiceHost serviceHost = new ServiceHost(typeof(TestService));
ServiceAuthorizationBehavior behavior =
serviceHost.Description.Behaviors.Find<ServiceAuthorizationBehavior>();
behavior.ImpersonateCallerForAllOperations = true;
If you use this technique, whether impersonation is done depends on a combination of the ImpersonateCallerForAllOperations property and the ImpersonationOptionOperationBehavior element. The matrix shown in Table 6 illustrates the combinations of values and the impersonation outcome. in the
Table 6. Outcome of Combined Impersonation Option Settings
ImpersonationOption | ImpersonateCallerFor AllOperations | Outcome |
---|
Required | true or false | Caller is impersonated. |
Allowed | false | Caller is not impersonated. |
Allowed | true | Caller is impersonated. |
NotAllowed | false | Caller is not impersonated. |
NotAllowed | true | An InvalidOperationException is thrown. |